#include "record_search_cache.h"
#include <fstream>
#include <sstream>
#include <chrono>
#include <iomanip>
#include <sys/stat.h>
#include <sys/types.h>
#include <libgen.h>
#include <errno.h>
#include <string.h>
#include <functional>
#include "base_log.h"
#include "common_config_manager.h"
#include "base_file_util.h"
#include "record_utils.h"
#include "base_time_cost.h"
#include <unordered_set>

namespace El {
namespace Record {

RecordSearchCache &RecordSearchCache::GetInstance()
{
    static RecordSearchCache instance;
    return instance;
}

RecordSearchCache::RecordSearchCache()
{
    // 从配置中获取存储路径
    auto config = Common::ConfigManager::GetInstance().GetConfig("storage");
    if (!config.empty() && config.contains("mountPath")) {
        storePath_ = config["mountPath"].get<std::string>();
    } else {
        storePath_ = "/mnt/udisk";
        LOG_ERROR("storage config is empty");
    }
    PathUtils::GetInstance().SetStorePath(storePath_);
}

bool RecordSearchCache::Start()
{
    if (running_) {
        LOG_WARN("RecordSearchCache already started");
        return true;
    }
    running_ = true;

    LOG_INFO("RecordSearchCache started");
    monitorThread_ = std::make_unique<std::thread>(&RecordSearchCache::MonitorThread, this);

    return true;
}

void RecordSearchCache::Stop()
{
    if (!running_) {
        LOG_WARN("RecordSearchCache already stopped");
        return;
    }
    running_ = false;

    LOG_INFO("RecordSearchCache stop");
    if (monitorThread_ && monitorThread_->joinable()) {
        monitorThread_->join();
    }

    std::lock_guard<std::mutex> lock(cacheMutex_);
    cache_.clear();
}

std::vector<VideoFileInfo> RecordSearchCache::SearchVideo(int32_t ch, TimePoint start, TimePoint end)
{
    std::vector<VideoFileInfo> results;

    // 将时间戳转换为本地时间
    time_t startT = std::chrono::system_clock::to_time_t(start);
    time_t endT = std::chrono::system_clock::to_time_t(end);

    // 获取日期字符串 YYYYMMDD
    char dateStr[9];
    time_t currentT = startT;
    struct tm *currentTm;

    while (currentT <= endT) {
        currentTm = localtime(&currentT);
        strftime(dateStr, sizeof(dateStr), "%Y%m%d", currentTm);
        std::string currentDate(dateStr);

        // 检查缓存
        std::lock_guard<std::mutex> lock(cacheMutex_);
        auto it = cache_.find(currentDate);
        if (it != cache_.end()) {
            const auto &index = it->second;
            for (const auto &video : index["videos"]) {

                // 校验video是否有效
                if (!video.contains("start_time") || !video.contains("filename")) {
                    LOG_DEBUG("video is invalid: {}", video.dump());
                    continue;
                }

                time_t videoStart = static_cast<time_t>(video["start_time"].get<uint32_t>());
                time_t videoEnd = videoStart + 1;
                if (video.contains("end_time")) {
                    videoEnd = static_cast<time_t>(video["end_time"].get<uint32_t>());
                } else {
                    LOG_DEBUG("video end time is not found: {}", video.dump());
                }

                if (video["channel"].get<int32_t>() == ch && videoStart <= endT && videoEnd >= startT) {
                    VideoFileInfo info;
                    info.ch = ch;
                    info.start = std::chrono::system_clock::from_time_t(videoStart);
                    info.end = std::chrono::system_clock::from_time_t(videoEnd);
                    if (video.contains("file_size")) {
                        info.size = video["file_size"].get<uint32_t>();
                    }
                    if (video.contains("stream_type")) {
                        info.type = video["stream_type"].get<int32_t>();
                    }
                    info.fileName =
                        PathUtils::GetInstance().GetVideoFilePath(currentDate, video["filename"].get<std::string>());

                    results.push_back(info);
                }
            }
        }

        // 增加一天的秒数
        currentT += 24 * 60 * 60;
    }

    return results;
}

std::vector<ImageFileInfo> RecordSearchCache::SearchImage(int32_t ch, TimePoint start, TimePoint end)
{
    std::vector<ImageFileInfo> results;

    time_t startT = std::chrono::system_clock::to_time_t(start);
    time_t endT = std::chrono::system_clock::to_time_t(end);

    char dateStr[9];
    time_t currentT = startT;
    struct tm *currentTm;

    while (currentT <= endT) {
        currentTm = localtime(&currentT);
        strftime(dateStr, sizeof(dateStr), "%Y%m%d", currentTm);
        std::string currentDate(dateStr);

        std::lock_guard<std::mutex> lock(cacheMutex_);
        auto it = cache_.find(currentDate);
        if (it != cache_.end()) {
            const auto &index = it->second;
            for (const auto &image : index["images"]) {
                time_t imageTime = static_cast<time_t>(image["timestamp"].get<uint32_t>());

                if (image["channel"].get<int32_t>() == ch && imageTime >= startT && imageTime <= endT) {
                    ImageFileInfo info;
                    info.ch = ch;
                    info.time = std::chrono::system_clock::from_time_t(imageTime);
                    info.size = image["file_size"].get<uint32_t>();
                    info.fileName =
                        PathUtils::GetInstance().GetImageFilePath(currentDate, image["filename"].get<std::string>());

                    results.push_back(info);
                }
            }
        }

        currentT += 24 * 60 * 60;
    }

    return results;
}

void RecordSearchCache::LoadDayIndex(const std::string &date)
{
    std::string indexPath = PathUtils::GetInstance().GetIndexFilePath(date);
    auto index = PathUtils::GetInstance().LoadIndex(indexPath);
    if (index.empty()) {
        LOG_ERROR("Failed to load index file: {}", indexPath);
        return;
    }

    bool indexModified = false;

    // 检查视频文件
    auto &videos = index["videos"];
    for (auto it = videos.begin(); it != videos.end();) {
        std::string fileName = PathUtils::GetInstance().GetVideoFilePath(date, (*it)["filename"].get<std::string>());

        if (!Base::IsFileExists(fileName)) {
            LOG_WARN("Video file not found, removing from index: {}", fileName);
            it = videos.erase(it);
            indexModified = true;
        } else {
            ++it;
        }
    }

    // 检查图片文件
    auto &images = index["images"];
    for (auto it = images.begin(); it != images.end();) {
        std::string fileName = PathUtils::GetInstance().GetImageFilePath(date, (*it)["filename"].get<std::string>());

        if (!Base::IsFileExists(fileName)) {
            LOG_WARN("Image file not found, removing from index: {}", fileName);
            // 同时检查并删除缩略图
            std::string thumbPath = PathUtils::GetInstance().GenerateThumbnailPath(fileName);
            if (Base::IsFileExists(thumbPath)) {
                unlink(thumbPath.c_str());
                LOG_INFO("Deleted orphaned thumbnail: {}", thumbPath);
            }
            it = images.erase(it);
            indexModified = true;
        } else {
            ++it;
        }
    }

    // 如果有修改，保存更新后的索引文件
    if (indexModified) {
        if (!PathUtils::GetInstance().WriteIndex(indexPath, index)) {
            LOG_ERROR("Failed to save index file: {}", indexPath);
        }
    }

    // 将索引添加到缓存
    std::lock_guard<std::mutex> lock(cacheMutex_);
    cache_[date] = std::move(index);
}

void RecordSearchCache::CleanExpiredCache()
{
    constexpr size_t MAX_CACHE_SIZE = 100 * 1024 * 1024; // 100MB
    size_t totalSize = 0;

    std::lock_guard<std::mutex> lock(cacheMutex_);

    // 计算缓存总大小并按日期排序
    std::multimap<std::string, std::string> sortedCache;
    for (const auto &[date, index] : cache_) {
        sortedCache.insert({date, date});
        // 估算 JSON 对象大小
        totalSize += index.dump().size();
    }

    // 如果超过最大缓存大小，从最早的日期开始清理
    while (totalSize > MAX_CACHE_SIZE && !sortedCache.empty()) {
        auto oldestIt = sortedCache.begin();
        auto cacheIt = cache_.find(oldestIt->second);
        if (cacheIt != cache_.end()) {
            totalSize -= cacheIt->second.dump().size();
            cache_.erase(cacheIt);
        }
        sortedCache.erase(oldestIt);
    }
}

void RecordSearchCache::MonitorThread()
{
    // 在后台线程中加载所有索引
    LoadAllIndices();

    while (running_) {
        // 清理过期缓存
        CleanExpiredCache();

        // 遍历存储目录，检查所有索引文件
        if (running_) { // 检查是否需要继续遍历
            PathUtils::GetInstance().TraverseStorageDirectory(
                [this](const std::string &date, const std::string &dayPath) {
                    if (running_) { // 检查每个处理是否需要继续
                        // 扫描当前目录下的所有文件，并更新索引
                        // 如果录像和图片文件存在，则更新索引
                        // 如果录像和图片文件不存在，则删除索引
                        ScanDirectory(date, dayPath);
                    }
                });
        }

        // 每分钟检查一次
        for (int i = 0; i < 60 && running_; ++i) {
            std::this_thread::sleep_for(std::chrono::seconds(1));
        }

        if (!running_) {
            break;
        }
    }
}

void RecordSearchCache::LoadAllIndices()
{
    LOG_INFO("Start loading all indices from storage path: {}", storePath_);

    int totalIndices = 0;
    PathUtils::GetInstance().TraverseStorageDirectory(
        [this, &totalIndices](const std::string &date, const std::string &dayPath) {
            LOG_INFO("Processing directory: {}", dayPath);
            LoadDayIndex(date);
            totalIndices++;
        });

    LOG_INFO("Finished loading all indices, total loaded: {}", totalIndices);

    // 打印当前缓存中的内容摘要
    std::lock_guard<std::mutex> lock(cacheMutex_);
    LOG_INFO("Cache summary - total dates in cache: {}", cache_.size());
    for (const auto &[date, index] : cache_) {
        size_t videoCount = index["videos"].size();
        size_t imageCount = index["images"].size();
        LOG_INFO("Date: {} - Videos: {}, Images: {}", date, videoCount, imageCount);
    }
}

bool RecordSearchCache::AddVideoFile(const nlohmann::json &info)
{
    // 获取文件对应的日期
    time_t fileTime = info["start_time"].get<int64_t>();
    struct tm *tm = localtime(&fileTime);

    char dateStr[9];
    strftime(dateStr, sizeof(dateStr), "%Y%m%d", tm);
    std::string date(dateStr);

    std::lock_guard<std::mutex> lock(cacheMutex_);
    if (cache_.find(date) == cache_.end()) {
        cache_[date] = nlohmann::json{{"videos", nlohmann::json::array()}, {"images", nlohmann::json::array()}};
    }
    cache_[date]["videos"].push_back(info);
    return true;
}

bool RecordSearchCache::AddImageFile(const nlohmann::json &info)
{
    // 获取文件对应的日期
    time_t fileTime = info["timestamp"].get<int64_t>();
    struct tm *tm = localtime(&fileTime);

    char dateStr[9];
    strftime(dateStr, sizeof(dateStr), "%Y%m%d", tm);
    std::string date(dateStr);

    std::lock_guard<std::mutex> lock(cacheMutex_);
    if (cache_.find(date) == cache_.end()) {
        cache_[date] = nlohmann::json{{"videos", nlohmann::json::array()}, {"images", nlohmann::json::array()}};
    }
    cache_[date]["images"].push_back(info);
    return true;
}

void RecordSearchCache::RemoveFileFromIndex(const FileToDelete &file,
                                            std::unordered_map<std::string, nlohmann::json> &modifiedIndices)
{
    std::lock_guard<std::mutex> lock(cacheMutex_);
    auto &index = cache_[file.date];
    bool indexModified = false;

    if (file.isVideo) {
        auto &videos = index["videos"];
        for (auto it = videos.begin(); it != videos.end(); ++it) {
            if ((*it)["filename"] == file.filename) {
                videos.erase(it);
                indexModified = true;
                break;
            }
        }
    } else {
        auto &images = index["images"];
        for (auto it = images.begin(); it != images.end(); ++it) {
            if ((*it)["filename"] == file.filename) {
                images.erase(it);
                indexModified = true;
                break;
            }
        }
    }

    if (indexModified) {
        modifiedIndices[file.date] = index;
    }
}

bool RecordSearchCache::SaveModifiedIndex(const std::string &date, const nlohmann::json &index)
{
    std::string indexPath = PathUtils::GetInstance().GetIndexFilePath(date);
    std::string jsonStr = index.dump(4);
    std::vector<uint8_t> content(jsonStr.begin(), jsonStr.end());

    if (!El::Base::SaveFileWithBackup(indexPath, content)) {
        LOG_ERROR("Failed to save index file: {}", indexPath);
        return false;
    }
    LOG_INFO("Updated index file: {}", indexPath);
    return true;
}

bool RecordSearchCache::HandleFileDelete(const FileToDelete &file,
                                         std::unordered_map<std::string, nlohmann::json> &modifiedIndices,
                                         bool isFileNotFound)
{
    // 尝试删除缩略图
    struct stat st;
    std::string thumbPath = PathUtils::GetInstance().GenerateThumbnailPath(file.path);
    if (stat(thumbPath.c_str(), &st) == 0) {
        if (unlink(thumbPath.c_str()) == 0) {
            LOG_INFO("Deleted {} thumbnail file: {}", isFileNotFound ? "orphaned" : "", thumbPath);

            // 检查并清理空的thumbnails目录
            std::string thumbDir = PathUtils::GetInstance().GetParentPath(thumbPath);
            if (PathUtils::GetInstance().IsDirectoryEmpty(thumbDir)) {
                if (rmdir(thumbDir.c_str()) == 0) {
                    LOG_INFO("Deleted empty thumbnails directory: {}", thumbDir);
                } else {
                    LOG_WARN("Failed to delete empty thumbnails directory: {}, error: {}", thumbDir, strerror(errno));
                }
            }
        } else {
            LOG_WARN("Failed to delete {} thumbnail file: {}, error: {}", isFileNotFound ? "orphaned" : "", thumbPath,
                     strerror(errno));
        }
    }

    // 从索引中删除文件记录
    RemoveFileFromIndex(file, modifiedIndices);

    // 检查索引是否为空
    auto indexIt = modifiedIndices.find(file.date);
    if (indexIt != modifiedIndices.end()) {
        const auto &index = indexIt->second;
        if (index["videos"].empty() && index["images"].empty()) {
            // 索引为空，删除索引文件
            std::string indexPath = PathUtils::GetInstance().GetIndexFilePath(file.date);
            if (unlink(indexPath.c_str()) == 0) {
                LOG_INFO("Deleted empty index file: {}", indexPath);
                modifiedIndices.erase(indexIt); // 从修改列表中移除，因为文件已被删除
            } else {
                LOG_ERROR("Failed to delete empty index file: {}, error: {}", indexPath, strerror(errno));
            }
        }
    }

    // 清理空目录
    std::string parentPath = PathUtils::GetInstance().GetParentPath(file.path);
    while (parentPath != storePath_) {
        if (!PathUtils::GetInstance().IsDirectoryEmpty(parentPath)) {
            break;
        }
        if (rmdir(parentPath.c_str()) != 0) {
            LOG_ERROR("Failed to remove directory: {} error: {}", parentPath, strerror(errno));
            break;
        }
        LOG_INFO("Deleted empty directory: {}", parentPath);
        parentPath = PathUtils::GetInstance().GetParentPath(parentPath);
    }

    // 保存修改过的索引文件
    bool success = true;
    for (const auto &[date, index] : modifiedIndices) {
        if (!SaveModifiedIndex(date, index)) {
            success = false;
        }
    }
    return success;
}

bool RecordSearchCache::DeleteOldestFile()
{
    std::vector<FileToDelete> allFiles;
    std::unordered_map<std::string, nlohmann::json> modifiedIndices;
    {
        std::lock_guard<std::mutex> lock(cacheMutex_);

        // 从缓存中收集所有文件信息
        for (const auto &[date, index] : cache_) {
            // 收集视频文件
            for (const auto &video : index["videos"]) {
                TimePoint videoTime =
                    std::chrono::system_clock::from_time_t(static_cast<time_t>(video["start_time"].get<uint32_t>()));
                std::string fileName =
                    PathUtils::GetInstance().GetVideoFilePath(date, video["filename"].get<std::string>());
                allFiles.push_back({videoTime, fileName, true, date, video["filename"].get<std::string>()});
            }

            // 收集图片文件
            for (const auto &image : index["images"]) {
                TimePoint imageTime =
                    std::chrono::system_clock::from_time_t(static_cast<time_t>(image["timestamp"].get<uint32_t>()));
                std::string fileName =
                    PathUtils::GetInstance().GetImageFilePath(date, image["filename"].get<std::string>());
                allFiles.push_back({imageTime, fileName, false, date, image["filename"].get<std::string>()});
            }
        }
    }

    // 如果没有找到任何文件
    if (allFiles.empty()) {
        LOG_INFO("No files found in cache");
        return false;
    }

    // 按时间排序
    std::sort(allFiles.begin(), allFiles.end(),
              [](const FileToDelete &a, const FileToDelete &b) { return a.time < b.time; });

    // 尝试删除文件，直到成功或尝试完所有文件
    for (const auto &file : allFiles) {
        struct stat st;
        if (stat(file.path.c_str(), &st) != 0) {
            LOG_WARN("File not found, trying next: {}", file.path);
            HandleFileDelete(file, modifiedIndices, true);
            continue;
        }

        try {
            // 检查文件是否正在使用
            if (Base::IsFileInUse(file.path)) {
                LOG_WARN("File is in use, skipping: {}", file.path);
                continue;
            }

            if (unlink(file.path.c_str()) != 0) {
                LOG_WARN("Failed to delete file: {}, error: {}, trying next", file.path, strerror(errno));
                continue;
            }

            LOG_INFO("Deleted oldest {} file: {}", file.isVideo ? "video" : "image", file.path);
            if (HandleFileDelete(file, modifiedIndices, false)) {
                return true;
            }
        } catch (const std::exception &e) {
            LOG_ERROR("Error while deleting file {}: {}, trying next", file.path, e.what());
            continue;
        }
    }

    // 如果所有文件都找不到或删除失败，保存修改过的索引
    if (!modifiedIndices.empty()) {
        bool success = true;
        for (const auto &[date, index] : modifiedIndices) {
            if (!SaveModifiedIndex(date, index)) {
                success = false;
            }
        }
        if (!success) {
            LOG_ERROR("Failed to save some modified indices");
        }
    }

    LOG_ERROR("Failed to delete any files after trying all candidates");
    return false;
}

void RecordSearchCache::ScanDirectory(const std::string &date, const std::string &dayPath)
{
    Base::TimeCost tc("ScanDirectory " + date, true);

    // 获取当前索引
    std::string indexPath = PathUtils::GetInstance().GetIndexFilePath(date);
    auto index = PathUtils::GetInstance().LoadIndex(indexPath);
    if (index.empty()) {
        index = nlohmann::json{{"videos", nlohmann::json::array()}, {"images", nlohmann::json::array()}};
    }

    bool indexModified = false;
    std::unordered_set<std::string> existingFiles;

    // 扫描视频文件
    std::string videoPath = dayPath + "/videos";
    if (Base::IsDirectoryExists(videoPath)) {
        Base::TraverseDirectory(videoPath, [&](const std::string &filename) {
            if (filename.length() >= 4 && filename.compare(filename.length() - 4, 4, ".mp4") == 0) {
                std::string fullPath = videoPath + "/" + filename;
                existingFiles.insert(filename);

                // 检查文件是否已在索引中
                bool found = false;
                for (const auto &video : index["videos"]) {
                    if (video["filename"] == filename) {
                        found = true;
                        break;
                    }
                }

                // 如果文件不在索引中，添加它
                if (!found) {
                    try {
                        auto videoInfo = PathUtils::GetInstance().ParseVideoFilename(filename);
                        if (!videoInfo.empty()) {
                            struct stat st;
                            if (stat(fullPath.c_str(), &st) == 0) {
                                videoInfo["file_size"] = static_cast<uint32_t>(st.st_size);
                            }
                            index["videos"].push_back(videoInfo);
                            indexModified = true;
                            LOG_INFO("Added new video file to index: {}", filename);
                        }
                    } catch (const std::exception &e) {
                        LOG_ERROR("Failed to parse video filename {}: {}", filename, e.what());
                    }
                }
            }
            return true;
        });
    }

    // 扫描图片文件
    std::string imagePath = dayPath + "/images";
    if (Base::IsDirectoryExists(imagePath)) {
        Base::TraverseDirectory(imagePath, [&](const std::string &filename) {
            if (filename.length() >= 4 && filename.compare(filename.length() - 4, 4, ".jpg") == 0) {
                std::string fullPath = imagePath + "/" + filename;
                existingFiles.insert(filename);

                // 检查文件是否已在索引中
                bool found = false;
                for (const auto &image : index["images"]) {
                    if (image["filename"] == filename) {
                        found = true;
                        break;
                    }
                }

                // 如果文件不在索引中，添加它
                if (!found) {
                    try {
                        auto imageInfo = PathUtils::GetInstance().ParseImageFilename(filename);
                        if (!imageInfo.empty()) {
                            struct stat st;
                            if (stat(fullPath.c_str(), &st) == 0) {
                                imageInfo["file_size"] = static_cast<uint32_t>(st.st_size);
                            }
                            index["images"].push_back(imageInfo);
                            indexModified = true;
                            LOG_INFO("Added new image file to index: {}", filename);
                        }
                    } catch (const std::exception &e) {
                        LOG_ERROR("Failed to parse image filename {}: {}", filename, e.what());
                    }
                }
            }
            return true;
        });
    }

    // 从索引中删除不存在的文件
    auto &videos = index["videos"];
    for (auto it = videos.begin(); it != videos.end();) {
        if (existingFiles.find((*it)["filename"].get<std::string>()) == existingFiles.end()) {
            LOG_INFO("Removing non-existent video from index: {}", (*it)["filename"].get<std::string>());
            it = videos.erase(it);
            indexModified = true;
        } else {
            ++it;
        }
    }

    auto &images = index["images"];
    for (auto it = images.begin(); it != images.end();) {
        if (existingFiles.find((*it)["filename"].get<std::string>()) == existingFiles.end()) {
            LOG_INFO("Removing non-existent image from index: {}", (*it)["filename"].get<std::string>());
            it = images.erase(it);
            indexModified = true;
        } else {
            ++it;
        }
    }

    // 如果索引被修改，保存它
    if (indexModified) {
        if (!PathUtils::GetInstance().WriteIndex(indexPath, index)) {
            LOG_ERROR("Failed to save index file: {}", indexPath);
            return;
        }

        // 更新缓存
        std::lock_guard<std::mutex> lock(cacheMutex_);
        cache_[date] = std::move(index);
        LOG_INFO("Updated index for date: {}", date);
    }
}

} // namespace Record
} // namespace El